#include "commonuse.h"
// int main()
// {
//     int a = 10;
//     scanf("%d", &a);//这里比较特殊，gcc编译器是支持变长数组的，但是不是所有编译器支持的
//     //这是c99的标准，不是所有编译器使用。
//     int arr[a];
//     system("pause");
//     return 0;

// }
//变长数组被当做特殊的局部变量，相对于普通局部变量，它的位置总是在栈的低地址处。
//但是一般也不用释放

// int main()
// {
//     int *arr = (int *)malloc(40);//为什么说
//     //这个这里必须要取整形指针，因为malloc函数返回的是一个地址
//     //返回的是void*的地址，需要强制转换，所以这份数组是以

//     //完全就是首元素地址表示的数组也就要注意越界访问
//     //malloc函数是向堆区申请一块空间，空间连续可用。

//     //开辟成功返回  开辟好空间的指针，注意这里void*，要强制类型转换
//     //开辟失败，返回NULL，返回失败原因很多，如堆不够大
//     //所以一定要检查，用strerro

//     //void不决定指针类型，使用者要自己决定，size为0，malloc委决定
//     //这点由编译器决定
//     free(arr);
//     arr = NULL;
//     system("pause");
//     return 0;
// }
// //free是专门用来释放动态内存和回收的，专门说明不能对非动态变量开🔪
// //malloc,动态内存开辟函数


//calloc
// int main()
// {
//     int *p = (int *)calloc(10,4);
//     //calloc使用差不多，但是前面个事元素个数，后面元素得大小
//     //同时全部初始化为0
//     system("pause");
//     return 0;
// }

//ralloc函数在返回新地址的时候就会直接释放原来的地址。
//realloc函数用来修改开辟得动态空间
//1.设p为已经开辟动态空间，如果其后面有足够空间直接追加空间，返回p地址
//2.如果p后面没有足够得空间追加，则realloc函数会重新找一个新的内存区域
//开辟一块满足需求得空间，并把原来内存中储存得数据拷贝过来，释放旧得空间
//最后返回新开辟空间得地址
//开辟失败返回空指针。这就意味着应该要拿另外一个指针接受
//

// int main()
// {
//     int *p =(int *) malloc(40);
//     for(int a = 0;a < 10;a++)
//     {
//         p[a] = ++a;
//     }
//     int *p1 =(int *) realloc(p,sizeof(int));//这里用第二个指针接受，这样即使返回空指针，也能够不丢失原来得地址。
//     printf("%s\n",strerror(errno));
//     system("pause");
//     return 0;
// }

// int main()
// {
//     int *p = realloc(NULL,20);
//     printf("%s\n",strerror(errno));
//     //发现这里noerror 说明realloc可以直接开辟空间。
//     system("pause");
//     return 0;
// }

//常见动态内存得操作错误，1.对空指针得解引用操作
//所以用malloc，calloc，开辟空间需要检验。
//2.对动态开辟空间得越界访问。就如同数组得越界访问
//3.对非动态内存得free释放，
//4.对部分动态内存的free释放
// int main()
// {
//     int *p = (int*) calloc(10,4);
//     if(!p)
//     {
//         return 0;
//     }
//     int i = 0;
//     for(i = 0;i < 10;i++)
//     {
//         *p++ = 1;
//     }

//     free(p);//发现这里就会报错，原因就是对内存的部分释放。
//     //在p++之后，p所指向的不再是动态内存所开辟的空间
//     //而是动态内存开辟的向后移动一部分，这个时候再释放，就会出问题
//     p = NULL;
//     return 0;
// }

//5.对同一块动态内存的多次（包括二次）释放。
//避免5的条错误，要保证谁开辟谁释放，还有就是对p = NULL;
//6.忘记释放，造成内存泄漏。很重要的一点，否则很消耗内存


// void getmemory(char * p)
// {
//     p = (char*)malloc(100);
// }

// void Test(void)
// {
//     char* str = NULL;
//     getmemory(str);
//     strcpy(str,"hello");
//     printf(str);
// }
// int main()
// {
//     Test();
//     system("pause");
//     return 0;
// }
//上述程序会报错，原因
//就是传过去的是是形式参数，
//注意传过去的是指针，对于指针指向的内容，确实是地址
//但是对于这份一级指针而言，传过去的是形式参数
//所以想改变这份指针就要去改变这份指针的指针，
//也就是二级指针传过去。

//修改如下
// void getmemory(char ** p)
// {
//     *p = (char*)malloc(100);

// }

// void Test(void)
// {
//     char* str = NULL;
//     getmemory(&(str));//这里传入指针的地址
//     strcpy(str,"hello");
//     printf(str);
//     free(str);//记得释放，避免内存泄露
// }
// int main()
// {
//     Test();
//     system("pause");
//     return 0;
// }


//第二种改变,就是使那一份地址能被记住
//那么返回拿分地址就可以。
// void* getmemory(char * p)
// {
//     p = (char*)malloc(100);
//     return p;
// }

// void Test(void)
// {
//     char* str = NULL;
//     str = getmemory(str);
//     strcpy(str,"hello");
//     printf(str);
//     free(str);
// }
// int main()
// {
//     Test();
//     system("pause");
//     return 0;
// }

// char* getmemory(void)
// {
//     char p[] = "hello world";
//     return 0;
// }//这块空间，在函数内部创建之后
// //但是当这个getmemory函数完之后
// //这块空间也就销毁，就不知道里面存的是什么
// //因为空间已经被销毁了。
// //所以**栈空间**的地址不要随便的返回，容易出错，里面都是2局部变量
// //所以非法访问，打印结果也未知5
// void test(void)
// {
//     char * str = NULL;
//     str = getmemory();
//     printf(str);
// }

// int main()
// {
//     test();
//     system("pause");
//     return 0;
// }

// int* test()
// {
//     static int a = 10;
//     return a;
// }
// //我们发现这种返回形式就是没有问题的
// //因为经过static的修饰，这份变量是存在**静态区**，也就不是栈区
// //那么就可以这样操作，但是，当没有用static修饰的时候就不能这样操作
// int main()
// {
//     system("pause");
//     return 0;
// }


//柔性数组
//柔性数组就是结构体最后一个元素是数组，且数组大小并不确定，且数组前面至少有一个其他元素。
//这种数组称之为柔性数组

struct S{
    int a;
    int arr[0];//在这个动态数组这里这里可以写0
};
//包含柔性数组成员的结构体，应该用动态内存，malloc去开辟空间
//注意自己开辟空间时对齐，大于结构体大小，且符合数组的预期
struct s{
    int a;
    int *d;
};//这个里面的d也可以去创建动态数组，但是那就不是柔性数组了
//开辟空间也不需要去创建动态空间。
//因此，其结构体变量申请的空间与其本身申请的动态空间也就没有什么关系
//如果结构体也要申请动态空间，那么他们动态空间就要分别释放，同时也说明二者空间不会在一起的
// int main()
// {
//     printf("%d\n", sizeof(struct S));//打印结果为4，
//     //这里就涉及了柔性数组的特性，在计算结构体大小和对齐数都不计算柔性数组，所以这也就是结构体不能只有柔性数组原因之一
//     struct S * p = (struct S *)malloc(sizeof(struct S)+ 9*sizeof(int));
//     p->arr[8] = 10;
//     printf("%d\n", p->arr[8]);//这里我们发现成功打印，没有越界访问，说明柔性数组开辟空间成功
//     //这一部分也说明柔性数组特色，利用结构体开辟动态内存所剩下的内存，作为柔性数组的空间
//     system("pause");
//     return 0;
// }

